/*
#Copyright (c) 2013, Philippe Bordron <philippe.bordron@gmail.com>
#
# This file is part of SIPPER.
#
# DCC is free software: you can redistribute it and/or modify
# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DCC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU LESSER GENERAL PUBLIC LICENSE for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# along with SIPPER.  If not, see <http://www.gnu.org/licenses/>
*/
package combi.wip.sipper.exec;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import combi.sipper.io.IOAssociation;
import combi.sipper.io.IOQueriesFile;
import combi.utils.Couple;
import combi.wip.graph.algo.ksp.LabelGroupeFromMap;
import combi.wip.graph.algo.ksp.srdrPaths.Ksp_Yen_SRDR_P2P_mt;

public class ComputeKSIPs {

	private static int nbParametresObligatoires = 8;



	/*
	 * starts sous la forme suivante
	 * 'label A'\t'sommet1' 'sommet2' ...
	 */

	/**
	 * @param args
	 * g : Graph
	 * starts : sources vertices
	 * ends : destination vertices
	 * k : number of paths between each source vertex in starts and each destination vertex in ends
	 * out : output file
	 * labels : vertices names
	 * nb_threads : number of threads
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		if (args.length < nbParametresObligatoires)
		{
			System.err.println("Incorrect parameters: parameters must be \n" +
					"g queries reactions k rankParameter lengthLimit out nb_threads\n"+
					"g : Graph\n" +
					"queries : file that contains the queries (sources reactions -> destination reactions)\n" +
					"reactions : list of vertices for each reaction\n" +
					"k : number of paths from each set of source verices in starts to each set of destination vertices\n" +
					"rankParameter : using k as ranking when the parameter is different to \"norank\", cut to de k-th path finded path if \"norank\"\n"+ 
					"lengthLimit : only keeps paths under the limit, not a number like \"nolimit\" to avoid it"+ 
					"out : output file\n" +
					//"labels : vertices names\n" +
					"nb_threads : number of threads");
			System.exit(1);
		}

		String graphFile = args[0];
		String queriesFile = args[1];
		//String sourcesFile = args[1];
		//String destFile = args[2];
		String verticesByReactions = args[2];
		int k = new Integer(args[3]);
		String rankParameter = args[4]; 
		String lengthLimit = args[5];
		String fileOut = args[6];
		//String nomGroupesFile = args[5];
		int	nbThreads = new Integer(args[7]);

		try {
			IOQueriesFile queriesIn = new IOQueriesFile(queriesFile);
			List<Couple<List<String>,List<String>>> queriesByReactions = queriesIn.read();
			Map<String, Set<Integer>> reactions = IOAssociation.loadInteger(verticesByReactions, "(?:(.+)\\t(.+))");
			List<Couple<Set<Integer>,Set<Integer>>> queries = generateQueriesByVertices(queriesByReactions, reactions);
			//Map<String, Set<Integer>> nomGroupes = loadAsso(nomGroupesFile, "(?:(.+)\\t(.+))");
			boolean useRank = true;
			double limit = Double.POSITIVE_INFINITY;
			if (rankParameter.equalsIgnoreCase("norank"))
			{
				useRank = false;
			}
			try{
				limit = Double.parseDouble(lengthLimit);
			}
			catch (Exception e)
			{
				limit = Double.POSITIVE_INFINITY;
			}
			Ksp_Yen_SRDR_P2P_mt algo = new Ksp_Yen_SRDR_P2P_mt(graphFile, queries, k, genereTwinsMap(reactions), useRank, fileOut);
			algo.setPathLengthLimit(limit);
			Ksp_Yen_SRDR_P2P_mt.set_NB_THREADS(Math.max(2, nbThreads+1));
			algo.setLabelsGroupes(new LabelGroupeFromMap<Set<Integer>>(inverseMap(reactions)));
			algo.run();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	
	private static List<Couple<Set<Integer>, Set<Integer>>> generateQueriesByVertices(
			List<Couple<List<String>, List<String>>> queriesByReactions,
			Map<String, Set<Integer>> reactions) {
		List<Couple<Set<Integer>, Set<Integer>>> result = new ArrayList<Couple<Set<Integer>, Set<Integer>>>(queriesByReactions.size());
		for (Couple<List<String>, List<String>> c : queriesByReactions)
		{
			Set<Integer> src = new HashSet<Integer>();
			for (String r : c.getFirst())
			{
				if (reactions.containsKey(r))
					src.addAll(reactions.get(r));
			}
			Set<Integer> dest = new HashSet<Integer>();
			for (String r : c.getSecond())
			{			
				if (reactions.containsKey(r))
					dest.addAll(reactions.get(r));
			}
			result.add(new Couple<Set<Integer>, Set<Integer>>(src,dest));
		}
		return result;
	}


	/***
	 * genereTwinsMap is a function that generate the map of twin vertices
	 * @param reactions is the map that associate for each reaction the corresponding set of vertices in Gint. 
	 * @return The map that contains that associate for each vertex its twin vertices
	 */
	private static Map<Integer, Set<Integer>> genereTwinsMap(Map<String, Set<Integer>> reactions)
	{
		Map<Integer, Set<Integer>> result = new HashMap<Integer, Set<Integer>>();
		for (Set<Integer> set : reactions.values())
		{
			for (Integer i : set)
			{
				result.put(i, set);
			}
		}
		return result;
	}

	// Il doit y avoir une bijection entre les deux partie de la map.
	private static Map<Set<Integer>, String> inverseMap(Map<String, Set<Integer>> map)
	{
		Map<Set<Integer>, String> newMap = new HashMap<Set<Integer>, String>();
		for (Entry<String, Set<Integer>> entry : map.entrySet())
		{
			String old = newMap.put(entry.getValue(), entry.getKey());
			if(old != null)
			{
				System.err.println(old);
			}
		}
		return newMap;
	}
}